﻿using Dapper;
using Microsoft.EntityFrameworkCore.Infrastructure;
using ProductServiceHost.Models;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database;

namespace ProductServiceHost.Controllers
{
    public class DemoController : BaseController
    {
        static ConcurrentDictionary<int, Models.Product> Products = new ConcurrentDictionary<int, Models.Product>();
        static Way.Lib.Collections.ConcurrentDictionaryActionQueue<int> UpdateQuantityQueue = new Way.Lib.Collections.ConcurrentDictionaryActionQueue<int>();
        /// <summary>
        /// 创建订单
        /// </summary>
        /// <param name="userid">购买人id</param>
        /// <param name="productId">产品id</param>
        /// <param name="quantity">产品数量</param>
        /// <returns>消费总金额</returns>
        public async Task<decimal> CreateOrder(int userid,int productId, int quantity)
        {
            //获取产品对应的内存对象
            var productInfo = Products.GetOrAdd(productId, _ =>
            {
                return this.CurrentDBContext.Connection.QueryFirst<Models.Product>("select Id,Quantity,PromotionId,Price from Product where Id=@Id", new
                {
                    Id = productId
                });
            });

            if (productInfo.Quantity >= quantity)
            {
                var nowQuantity = Interlocked.Add(ref productInfo.quantity, -quantity);
                if(nowQuantity < 0)
                {
                    //归还库存
                    Interlocked.Add(ref productInfo.quantity, quantity);
                    throw new ServiceException("产品数量不足");
                }

                //扣减库存成功, 标识自己支持分布式事务，以便分布式事务回滚时，这里能触发AfterRollback事件

                this.CurrentDBContext.BeginTransaction();//标识自己支持分布式事务

                this.CurrentDBContext.AfterCommit += (s, e) =>
                {
                    //如果提交事务成功，那么同步一下数据库的库存数量
                    UpdateQuantityQueue.Add(1, () => {
                        using (var db = new SystemDBContext())
                        {
                            db.Connection.Execute("update Product set Quantity=Quantity-@quantity where Id=@productId",new { productId,quantity });
                    }
                    });
                   
                };

                this.CurrentDBContext.AfterRollback += (s, e) => {
                    //如果分布式事务发生回滚，那么归还库存
                    Interlocked.Add(ref productInfo.quantity, quantity);
                };

                //生成待支付订单
                await this.CurrentDBContext.Connection.ExecuteAsync("insert into [Order] (UserId,ProductId,Quantity,PromotionId,Status) values (@UserId,@ProductId,@Quantity,@PromotionId,@Status)", new 
                {
                    ProductId = productId,
                    Quantity = quantity,
                    PromotionId = productInfo.PromotionId,
                    Status = 2,
                    UserId = userid
                } , (IDbTransaction)this.CurrentDBContext.CurrentTransaction);

                //最后不用提交事务，事务由JMS底层框架控制提交
                return productInfo.Price * quantity;
            }
            else
            {
                throw new ServiceException("产品数量不足");
            }
        }

        /// <summary>
        /// 添加产品
        /// </summary>
        /// <param name="product"></param>
        /// <returns>产品id</returns>
        public async Task<int> AddProduct(Product product)
        {
            //先判断产品是否存在
            if (await this.CurrentDBContext.Connection.ExecuteScalarAsync<int>("select count(*) from [Product] where Name=@Name", product) == 0)
            {
                //标识支持分布式事务
                this.CurrentDBContext.BeginTransaction();

                await this.CurrentDBContext.Connection.ExecuteAsync("insert into [Product] (Name,TotalQuantity,Quantity,PromotionId,Price) values (@Name,@TotalQuantity,@Quantity,@PromotionId,@Price)",
                    product, (IDbTransaction)this.CurrentDBContext.CurrentTransaction);

                return await this.CurrentDBContext.Connection.ExecuteScalarAsync<int>("select last_insert_rowid()", null, (IDbTransaction)this.CurrentDBContext.CurrentTransaction);
            }
            else
            {
                return await this.CurrentDBContext.Connection.ExecuteScalarAsync<int>("select Id from [Product] where Name=@Name", product);
            }
        }
    }
}
